package jmine.tec.datadigester.util;

import java.io.InputStream;
import java.text.SimpleDateFormat;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import bancosys.tec.datadigester.DataDigesterController;
import bancosys.tec.datadigester.decompressor.impl.ZIPDecompressor;
import bancosys.tec.persist.audit.AuditManager;
import bancosys.tec.persist.audit.AuditStatus;
import bancosys.tec.persist.authorization.AuthorizationManager;
import bancosys.tec.persist.authorization.AuthorizationStatus;
import bancosys.tec.persist.spring.transaction.PartitionTransactionDefinition;
import bancosys.tec.rtm.impl.RtmController;
import bancosys.tec.utils.date.Timestamp;

/**
 * Utilitário utilizado pelos MessageListener que consomem a fila gerada pelo data-feeder.
 * 
 * @author wagner
 */
public class DigesterMessageListenerUtil {
    private static final Logger LOGGER = LogManager.getLogger(DigesterMessageListenerUtil.class);

    private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyyMMddhhmmss");

    /**
     * Controller do datadigester responsável por transformar o arquivo em um DigesterBean.
     */
    private DataDigesterController digesterController;

    private RtmController rtmController;

    private AuthorizationManager authorizationManager;

    private AuditManager auditManager;

    private PlatformTransactionManager txManager;

    /**
     * @param digesterController the digesterController to set
     */
    public void setDigesterController(DataDigesterController digesterController) {
        this.digesterController = digesterController;
    }

    /**
     * @param rtmController the rtmController to set
     */
    public void setRtmController(RtmController rtmController) {
        this.rtmController = rtmController;
    }

    /**
     * @param authorizationManager the authorizationManager to set
     */
    public void setAuthorizationManager(AuthorizationManager authorizationManager) {
        this.authorizationManager = authorizationManager;
    }

    /**
     * @param auditManager the auditManager to set
     */
    public void setAuditManager(AuditManager auditManager) {
        this.auditManager = auditManager;
    }

    /**
     * @param txManager the txManager to set
     */
    public void setTxManager(PlatformTransactionManager txManager) {
        this.txManager = txManager;
    }

    /**
     * Chamado pelo container para tratar uma mensagem.
     * 
     * @param fileCode fileCode do arquivo.
     * @param physicalFile arquivo.
     * @throws Exception exceção.
     */
    public void processFile(String fileCode, InputStream physicalFile) throws Exception {
        LOGGER.info("Recuperado código do arquivo e conteúdo. FileCode: " + fileCode + ". PhysicalFile: " + physicalFile);

        PartitionTransactionDefinition txDefinition = new PartitionTransactionDefinition();
        txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus txStatus = this.txManager.getTransaction(txDefinition);
        try {
            this.setThreadAuthorizationStatus(AuthorizationStatus.DISABLED);
            this.setThreadAuditStatus(AuditStatus.DISABLED);
            String fileName = "DataFeeder-" + fileCode + "-" + DATE_FORMATTER.format(new Timestamp());
            this.digesterController.process(fileCode, fileName, physicalFile, new ZIPDecompressor());
            this.txManager.commit(txStatus);
        } catch (RuntimeException e) {
            this.rtmController.monitor(e);
            this.txManager.rollback(txStatus);
            throw e;
        } catch (Exception e) {
            this.rtmController.monitor(e);
            this.txManager.rollback(txStatus);
            throw e;
        } finally {
            this.setThreadAuthorizationStatus(AuthorizationStatus.ENABLED);
            this.setThreadAuditStatus(AuditStatus.ENABLED);
        }
    }

    /**
     * Seta o status da authorizacao para a thread corrente.
     * 
     * @param authorizationStatus authorizationStatus
     */
    private void setThreadAuthorizationStatus(AuthorizationStatus authorizationStatus) {
        this.authorizationManager.setThreadAuthorizationStatus(authorizationStatus);
    }

    /**
     * Seta o status da auditoria para a thread corrente.
     * 
     * @param auditStatus auditStatus
     */
    private void setThreadAuditStatus(AuditStatus auditStatus) {
        this.auditManager.setThreadAuditStatus(auditStatus);
    }
}
